﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using Comm.PublicClass;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.Serialization;

namespace WsProtocol.IEC62056
{
    // hxh 2018-5 62056加密对象类
    [DataContract]
    public class KMSEncrypt
    {
        [DllImport(".\\kms_access.dll")]
        static extern void init_context();

        [DllImport(".\\kms_access.dll")]
        static extern string kms_get_hls5challenge(string deviceSerialNumber, string manufacturer, out int result, out string challenge, out string errorCode, out string errorDescription);

        [DllImport(".\\kms_access.dll")]
        static extern void kms_hls5authentication(string deviceSerialNumber, string manufacturer, string oeRoleIdentifier, string hlsId, string challenge, string initialVector, out int result, out string authenticatedChallenge, out string errorCode, out string errorDescription);
        
        [DllImport(".\\kms_access.dll")]
        static extern void kms_encrypt_single(string deviceSerialNumber, string manufacturer, string oeRoleIdentifier, string securityControl, string initialVector, string encryptionSuite, string payloadHex, out int result, out string encryptedPayloadHex, out string encryptedAuthTagHex, out string errorCode, out string errorDescription);

        [DllImport(".\\kms_access.dll")]
        static extern void kms_decrypt_single(string deviceSerialNumber, string manufacturer, string oeRoleIdentifier, string securityControl, string initialVector, string encryptionSuite, string encryptedPayloadHex, string encryptedAuthTagHex, out int result, out string decryptedPayloadHex, out string decryptedAuthResult, out string errorCode, out string errorDescription);
        
        
        [DllImport(".\\kms_access.dll")] 
        static extern int aes_wrap_String(string pKey,string pPlaintext, StringBuilder pOutPlaintext);

        [DllImport(".\\kms_access.dll")] 
        static extern int aes_unwrap_string(string pKey, string pCipher, StringBuilder pOutPlaintext);

        [DataContract]
        public class EncryptKey  //SecuritySuite为0和1时 Key 16字节
        {
            public string MK { get; set; }
            public string EK { get; set; }
            public string BK { get; set; }
            public string AK { get; set; }
            public string DK { get; set; }
            // public string Rec_IV { get; set; }
        }

        //---------加密接口返回结果定义-------//
        private const int SYSTEM_FAULT = -1;
        private const int KMS_SYSTEM_FAULT = -2;
        private const int RESPONSE_FAULT = 0;
        private const int RESPONSE_OK = 1;
        //------------------------------------//


        private static int MaxBufferSize = 8 * 1028 * 1024;

        private int KeyLen = 16;
        private int TagLen = 12;

        private string IVector; //Sys-T + FC
        private string m_MeterNo;
        private string m_FactoryCode;
        private int m_IC;
        private SecuritySuite m_securitySuite; //一般固定为GCM_128

        public string Sys_T{get;set;}   //System Title=FactName+MeterNo.substring(MeterNo.Length-10,10) 5字节
        public RequirContext ContextName { get; set; }
        public Authentication apduAuth { get; set; }
        public SecurityPolicy securityPolicy { get; set; }
        public SecuritySuite securitySuite
        {
            get { return m_securitySuite; }
            set
            {
                m_securitySuite = value;
                switch (m_securitySuite)
                {
                    case SecuritySuite.GCM_128:
                    case SecuritySuite.GCM_128_SHA_256:
                        KeyLen = 16;
                        break;
                    case SecuritySuite.GCM_256_SHA_384:
                        KeyLen = 32;
                        break;

                }
            }
        }
        public EncryptKey Key { get; set; }
        public string MeterNo  //取5个字节 
        {
            get { return m_MeterNo; }
            set
            {

                if (!string.IsNullOrEmpty(value) && value.Length > 10)
                    m_MeterNo = value.Substring(value.Length - 10, 10);
                else
                    m_MeterNo = value.PadLeft(10, '0');              
            }

        }
        public string FactoryCode  //取3个字节
        {
            get { return m_FactoryCode; }
            set { }
        }
        public int IC   //4字节,帧计数器double_long_unsigned
        {
            get { 
                return m_IC;
            }
            set
            {
                m_IC = value;              
            }
        }

        public int Key_Set { get; set; }
        public bool NeedResponse = true;

        public string DeviceSerialNo = "";  //设备编码
        public string PlaintData;    //明文数据 
        public string EncryptData;  //密文数据
        public string TagStr;  //认证信息 即MAC
        public string RoleIdentifier = "";  //客户端角色


        public string CtoS { get; set; } //hxh 高级认证连接时第一步发送给表的随机数
        public string StoC { get; set; } // 高级认证连接后返回的数据加密后得到，需由64类的方法1发送给表
        public string AuthCtoS { get; set; } //由64类的方法1返回的数据再加密后得到的
        public KMSEncrypt()
        {
            ContextName = RequirContext.LN_NoCipher;
            apduAuth = Authentication.LLS;
            securityPolicy = SecurityPolicy.无认证;
            m_securitySuite = SecuritySuite.GCM_128;
            m_FactoryCode = "WSE";            
            Key_Set = 0;
            Key = new EncryptKey();
        }

        //复制自身
        public Encryption Copy()
        {
            //新建一个
            Encryption newmeter = new Encryption();
            //复制属性
            PropertyInfo[] fromFields = typeof(Encryption).GetProperties();
            PubFunction.SetProperties(fromFields, this, newmeter);
            return newmeter;
        }

        public bool IfEncrypt() {
            try
            {
                if ((ContextName == RequirContext.LN_Cipher || apduAuth >= Authentication.HLS)
                            && securityPolicy != SecurityPolicy.无认证)
                    return true;
                else
                    return false;
            }
            catch(Exception ex ){
                return false;
            }
        }

        public string GetSysTitle() {
            return  MyConvert.StrToHex(m_FactoryCode.Substring(0, 3)) + m_MeterNo;           
        }

        private string GetSControl(SecurityPolicy sPolicy, bool Keyset)
        {
            // Bit 7 	    Bit 6 	  Bit 5 Bit 4 	Bit 3..0 
            //Compression 	Key_Set 	E 	  A 	Security_Suite_Id=0000
            int keyset = Keyset ? Key_Set : 0; //进入加密参数时不用考虑Key_Set
            string sContr = keyset.ToString() + MyConvert.DecToBin(((int)sPolicy).ToString(), 2) + "0000";
            return MyConvert.BinToHex(sContr);
        }      


        private string GetFormatKey(string sKey)
        {

            string KeyStr = sKey;
            if (string.IsNullOrEmpty(sKey))
            {
                KeyStr = this.Key.EK;
                if (!NeedResponse || Key_Set==1)
                    KeyStr = this.Key.BK;
                if (!string.IsNullOrEmpty(this.Key.DK))
                    KeyStr = this.Key.DK;              

            }
            if (KeyStr.Length != KeyLen * 2)
                KeyStr = KeyStr.Length > KeyLen * 2 ? KeyStr.Substring(KeyStr.Length - KeyLen * 2, KeyLen * 2) : KeyStr.PadLeft(KeyLen * 2, '0');

            return KeyStr;
        }

        public string GCM_AESEncrypt(SecurityPolicy sPolicy, bool ICIncrease, ref string errorinfo) //固定用Authen方式加密
        {        
          try
          {
              return GCM_EncryptByKeyStr(sPolicy, "", ICIncrease, ref  errorinfo);

          }
          catch (Exception ex)
          {
              errorinfo = ex.ToString();
              return string.Empty;
          }
        }

        //public string GCM_EncryptByKeyStr(SecurityPolicy sPolicy, string EkKeyStr, bool ICIncrease, ref string errorinfo) //固定用Authen方式加密
        //{
        //   return  GCM_EncryptByKeyStr(sPolicy, EkKeyStr, ICIncrease, true, ref errorinfo);
        //}

        public string GCM_EncryptByKeyStr(SecurityPolicy sPolicy, string EkKeyStr, bool ICIncrease, ref string errorinfo) //固定用Authen方式加密
        {
            int iresult = 0;
            try
            {

                string KeyStr = GetFormatKey(EkKeyStr);

                this.Key.AK = GetFormatKey(this.Key.AK);
                string ADataStr = GetSControl(sPolicy,false) + this.Key.AK;

                string PlaintText = this.PlaintData;
                if (sPolicy == SecurityPolicy.需认证) //认证模式下的A－A ＝SC + AK+ PlaintData
                {
                    ADataStr = ADataStr + this.PlaintData;
                    PlaintText = string.Empty;
                }

                StringBuilder Cliphertext = new StringBuilder(MaxBufferSize);//加密输出8 * 1024 * 1024
                StringBuilder pOutTag = new StringBuilder(128);//tag

                if (ICIncrease)
                    m_IC++;

                IVector = GetSysTitle() + MyConvert.DecToHex(m_IC.ToString(), 8);

                string errcode = string.Empty;
                string challenge = string.Empty;
                kms_get_hls5challenge(DeviceSerialNo, m_FactoryCode, out iresult, out challenge, out errcode, out errorinfo); //获取challenge
                if (iresult != RESPONSE_OK)
                {
                    return string.Empty;
                }

                switch (sPolicy)
                {
                    case SecurityPolicy.需认证:
                        kms_hls5authentication(DeviceSerialNo, m_FactoryCode, RoleIdentifier, "HLS5", challenge, IVector, out iresult, out challenge, out errcode, out errorinfo);
                        break;
                    case SecurityPolicy.需加密:
                    case SecurityPolicy.需认证加密:
                        EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + Cliphertext.ToString().ToUpper() + TagStr;
                        break;

                }

                //iresult = Encrypt_StringData(KeyStr, IVector, ADataStr, PlaintText, Cliphertext, pOutTag); //进行加密操作
                if (iresult > 0)
                {
                    if (sPolicy == SecurityPolicy.需加密) //只加密模式无Tag
                        TagStr = "";
                    else
                        TagStr = pOutTag.ToString().ToUpper();
                    if (string.IsNullOrEmpty(Cliphertext.ToString()) && apduAuth != Authentication.HLS_GMAC)
                        Cliphertext.Append(this.PlaintData);


                    switch (sPolicy)
                    {
                        case SecurityPolicy.需认证:
                            if (ICIncrease)
                                EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + this.PlaintData + TagStr;
                            else  //建连接时最后一步要用
                                EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + TagStr;
                            break;
                        case SecurityPolicy.需加密:
                        case SecurityPolicy.需认证加密:
                            EncryptData = GetSControl(sPolicy, true) + MyConvert.DecToHex(m_IC.ToString(), 8) + Cliphertext.ToString().ToUpper() + TagStr;
                            break;

                    }
                    
                }
                else
                    EncryptData = string.Empty;                

                return EncryptData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public string GCM_AESEncrypt(ref string errorinfo)
        {
            return GCM_AESEncrypt(securityPolicy,true,ref  errorinfo);           

        }

        public string GCM_AESDecrypt(int EKFlag, ref string errorinfo)
        {
            errorinfo = string.Empty;
            try
            {
                string KeyStr = EKFlag == 0 ? Key.EK : Key.DK;
                KeyStr = GetFormatKey(KeyStr);

                this.Key.AK = GetFormatKey(this.Key.AK);                     

                string Valuestr = this.EncryptData;
                SecurityPolicy sPolicy = (SecurityPolicy)int.Parse(Valuestr.Substring(0, 1));
                Valuestr = Valuestr.Substring(2); //去掉加密模式标志               
                string ADataStr = GetSControl(sPolicy, false) + this.Key.AK;

                string IVector = Sys_T + Valuestr.Substring(0, 8); //以本次返回的 Sys_T和FC去解密               

                Valuestr = Valuestr.Substring(8); //去掉FC字节               
               
                //取得TAG
                StringBuilder mTagStr = new StringBuilder(128);
                mTagStr.Append(this.TagStr);
                if (sPolicy == SecurityPolicy.需认证 || sPolicy == SecurityPolicy.需认证加密)
                {
                    TagStr = Valuestr.Substring(Valuestr.Length - TagLen * 2, TagLen * 2);
                    Valuestr = Valuestr.Substring(0, Valuestr.Length - TagLen * 2);
                    EncryptData = Valuestr;
                }

                if (securityPolicy == SecurityPolicy.需认证) //认证模式下的A－A ＝SC + AK+ PlaintData
                {
                    ADataStr += EncryptData;
                    Valuestr = "";
                }
                StringBuilder Plaintext = new StringBuilder(MaxBufferSize);//解密输出  8 * 1024 * 1024
                int iresult = 0;
                //int iresult = Decrypt_StringData(KeyStr, IVector, ADataStr, Valuestr, mTagStr, Plaintext); //进行解密操作
                if (iresult > 0)
                {
                    //if (!string.IsNullOrEmpty(TagStr) && string.Compare(TagStr, mTagStr.ToString().ToUpper()) != 0)
                    //    errorinfo = clsLan.GetLan("数据验证失败");

                    PlaintData = Plaintext.ToString().ToUpper();
                    if (string.IsNullOrEmpty(PlaintData))
                        PlaintData = EncryptData;
                }
                else
                {
                    PlaintData = string.Empty;
                    errorinfo = "数据验证失败"; //shaunwang 2019-9-9
                }

                return PlaintData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        /// <summary>
        /// 密钥加密,即第一次加密Aes算法
        /// </summary>
        /// <param name="sKey"></param>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        public string Aes_wrap_String(string sKey, ref string errorinfo)
        {
            StringBuilder Cliphertext = new StringBuilder(MaxBufferSize);//加密输出
            try
            {
                int iresult = aes_wrap_String(sKey, PlaintData, Cliphertext); //进行加密操作
                if (iresult == 0)
                {
                    EncryptData = Cliphertext.ToString().ToUpper();
                }
                else
                    EncryptData = string.Empty;

                return EncryptData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        public string Aes_unwrap_String(string sKey, ref string errorinfo)
        {
            errorinfo = string.Empty;
            try
            {              
                StringBuilder Plaintext = new StringBuilder(MaxBufferSize);//解密输出
                int iresult = aes_unwrap_string(sKey, EncryptData, Plaintext); //进行解密操作
                if (iresult== 0)
                {                  
                    PlaintData = Plaintext.ToString().ToUpper();                   
                }
                else
                {
                    PlaintData = string.Empty;
                    errorinfo = "数据解密失败";
                }
                return PlaintData;
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

    }


    

}
